﻿using Windows.Storage;
using Windows.System;
using Windows.UI.ViewManagement;
using DimensionsHelper.Common.Contracts.TableFormatter;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Metadata;
using DimensionsHelper.Common.Contracts.Table;
using DimensionsHelper.Services.TableFormatter;
using DimensionsHelper.Services.CsvReader;
using DimensionsHelper.Services.Excel.EPPlus;
using DimensionsHelper.Services.Metadata;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
using UnhandledExceptionEventArgs = Microsoft.UI.Xaml.UnhandledExceptionEventArgs;

namespace DimensionsHelper.Client.WinUi;

public sealed partial class App : Application
{
    private const string IsNavigationLeftModeKey = "IsNavigationLeftMode";
    private const string NotifyWhenTaskFinishedKey = "NotifyWhenTaskFinished";
    private const string HistoryKeptNumberKey = "HistoryKeptNnumber";

    private static bool _isNavigationLeftMode = true;
    private static bool _notifyWhenTaskFinished = true;

    private static readonly List<RecordHistoryViewModel> RecordHistoryViewModels = [];


    public App()
    {
        InitializeComponent();

        Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory)
            .ConfigureServices((context, services) =>
            {
                // Default Activation Handler
                services.AddTransient<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>();

                // Other Activation Handlers
                services.AddTransient<IActivationHandler, AppNotificationActivationHandler>();

                // Services
                services.AddSingleton<IAppNotificationService, AppNotificationService>();
                services.AddSingleton<ILocalSettingsService, LocalSettingsService>();
                services.AddSingleton<IThemeSelectorService, ThemeSelectorService>();
                services.AddTransient<INavigationViewService, NavigationViewService>();

                services.AddSingleton<IActivationService, ActivationService>();
                services.AddSingleton<IPageService, PageService>();
                services.AddSingleton<INavigationService, NavigationService>();

                // Core Services
                services.AddSingleton<IFileService, FileService>();
                services.AddSingleton<IFilePikerService, FilePickerService>();
                services.AddSingleton<IMessageService, MessageService>();
                services.AddSingleton<IProjectTaskService, ProjectTaskService>();
                services.AddTransient<IOptionalFileService, OptionalFileService>();
                services.AddTransient<IExcelWorkbookOperator, ExcelWorkbookOperator>();
                services.AddTransient<ITableReader, CsvStreamReader>();
                services.AddTransient<ITableFormatter, TableFormatter>();
                services.AddTransient<IMetadataSerializer, MetadataSerializer>();

                // Views and ViewModels
                services.AddSingleton<QueryDataViewModel>();
                services.AddSingleton<QueryDataPage>();
                services.AddSingleton<TableReorderMapViewModel>();
                services.AddSingleton<TableReorderMapPage>();
                services.AddSingleton<TableFormatterFilesViewModel>();
                services.AddSingleton<TableFormatterFilesPage>();
                services.AddSingleton<TableFormatterTableOptionsViewModel>();
                services.AddSingleton<TableFormatterTableOptionsPage>();
                services.AddSingleton<QueryViewModel>();
                services.AddSingleton<QueryPage>();
                services.AddSingleton<MetadataDocumentViewModel>();
                services.AddSingleton<MetadataDocumentPage>();
                services.AddSingleton<TableReplacementsViewModel>();
                services.AddSingleton<TableReplacementsPage>();
                services.AddSingleton<SettingsViewModel>();
                services.AddSingleton<SettingsPage>();
                services.AddSingleton<TaskInformationViewModel>();
                services.AddSingleton<TaskInformationPage>();
                services.AddSingleton<TableFormatterViewModel>();
                services.AddSingleton<TableFormatterPage>();
                services.AddSingleton<ShellPage>();
                services.AddSingleton<ShellViewModel>();

                // Configuration
                services.Configure<LocalSettingsOptions>(
                    context.Configuration.GetSection(nameof(LocalSettingsOptions)));
                services.Configure<PresetOptions>(context.Configuration.GetSection(nameof(PresetOptions)));
            }).Build();

        StartupWindow = null!;
        UiSettings = new UISettings();
        GlobalTask = GetService<IProjectTaskService>().Allocate(GlobalTaskId, false);

        UnhandledException += App_UnhandledException;
    }

    public static string GlobalTaskId => "全局";


    public static DispatcherQueue DispatcherQueue => MainWindow.DispatcherQueue;


    private UISettings UiSettings
    {
        get;
    }


    public static int HistoryKeptNumber
    {
        get
        {
            if (!RuntimeHelper.IsMSIX)
            {
                return field;
            }

            int value = GetAppDataSettings(HistoryKeptNumberKey, 20);

            if (value != 0)
            {
                return value;
            }

            SetAppDataSettings(HistoryKeptNumberKey, 20);
            return 20;
        }

        set
        {
            if (value <= 0)
            {
                return;
            }

            if (RuntimeHelper.IsMSIX)
            {
                int current = (int)GetAppDataSettings(HistoryKeptNumberKey);

                if (current == value)
                {
                    return;
                }

                SetAppDataSettings(HistoryKeptNumberKey, value);
            }
            else
            {
                if (field == value)
                {
                    return;
                }

                field = value;
            }

            ((App)Current)?.RaisePropertyChanged(nameof(HistoryKeptNumber), value);
        }
    } = 20;


    // The .NET Generic Host provides dependency injection, configuration, logging, and other services.
    // https://docs.microsoft.com/dotnet/core/extensions/generic-host
    // https://docs.microsoft.com/dotnet/core/extensions/dependency-injection
    // https://docs.microsoft.com/dotnet/core/extensions/configuration
    // https://docs.microsoft.com/dotnet/core/extensions/logging
    public IHost Host
    {
        get;
    }


    public Window StartupWindow
    {
        get;
        private set;
    }


    public static Window MainWindow => ((App)Current).StartupWindow;


    public static UIElement? AppTitleBarText
    {
        get;
        set;
    }

    public static FrameworkElement? AppTitleBar
    {
        get;
        set;
    }


    public IProjectTask GlobalTask
    {
        get;
    }


    private static void UISettings_ColorValuesChanged(UISettings sender, object args) =>
        DispatcherQueue.TryEnqueue(TitleBarHelper.ApplySystemThemeToCaptionButtons);


    public static void SetAppDataSettings<T>(string key, T value) =>
        ApplicationData.Current.LocalSettings.Values[key] = value;


    private static object GetAppDataSettings(string key) => ApplicationData.Current.LocalSettings.Values[key];


    public static T GetAppDataSettings<T>(string key, T defaultValue)
    {
        object localSetting = GetAppDataSettings(key);

        if (localSetting is not T targetTypeValue)
        {
            SetAppDataSettings(key, defaultValue);
            targetTypeValue = defaultValue;
        }

        return targetTypeValue;
    }


    public static bool IsCurrentNavigationLeftMode() => RuntimeHelper.IsMSIX
        ? GetAppDataSettings(IsNavigationLeftModeKey, true)
        : _isNavigationLeftMode;


    public static bool NotifyWhenTaskFinished() => RuntimeHelper.IsMSIX
        ? GetAppDataSettings(NotifyWhenTaskFinishedKey, true)
        : _notifyWhenTaskFinished;


    public event Action<string, object>? PropertyChanged;


    private void RaisePropertyChanged(string propertyName, object value) =>
        PropertyChanged?.Invoke(propertyName, value);


    public static void RegisterViewModel(RecordHistoryViewModel viewModel)
    {
        RecordHistoryViewModels.Add(viewModel);
        ((App)Current).PropertyChanged += viewModel.OnAppPropertyChanged;
    }


    public static void UnregisterViewModel(RecordHistoryViewModel viewModel)
    {
        RecordHistoryViewModels.Remove(viewModel);
        ((App)Current).PropertyChanged -= viewModel.OnAppPropertyChanged;
    }


    public static async void ClearAllHistory()
    {
        try
        {
            foreach (RecordHistoryViewModel viewModel in RecordHistoryViewModels)
            {
                await viewModel.ClearHistoricalListAsync();
            }
        }
        catch (Exception e)
        {
            await ShowErrorMessage(e);
        }
    }


    public static IEnumerable<RecordHistoryViewModel> GetRecordHistoryViewModels() =>
        RecordHistoryViewModels.AsEnumerable();


    public static void SetCurrentNavigationMode(bool isLeftMode)
    {
        if (MainWindow.Content is not ShellPage shellPage)
        {
            return;
        }

        NavigationView navigationView = shellPage.NavigationView;
        if (isLeftMode)
        {
            navigationView.PaneDisplayMode = NavigationViewPaneDisplayMode.Auto;
            Grid.SetRow(navigationView, 0);

            if (AppTitleBar != null)
            {
                Grid.SetColumn(AppTitleBar, 1);
                Grid.SetColumnSpan(AppTitleBar, 1);
            }
        }
        else
        {
            navigationView.PaneDisplayMode = NavigationViewPaneDisplayMode.Top;
            Grid.SetRow(navigationView, 1);

            if (AppTitleBar != null)
            {
                Grid.SetColumn(AppTitleBar, 0);
                Grid.SetColumnSpan(AppTitleBar, 2);
            }
        }

        shellPage.UpdateSettingItemContent(!isLeftMode);

        if (RuntimeHelper.IsMSIX)
        {
            ApplicationData.Current.LocalSettings.Values[IsNavigationLeftModeKey] = isLeftMode;
        }
        else
        {
            _isNavigationLeftMode = isLeftMode;
        }
    }


    public static void SetCurrentNotifyMode(bool notifyWhenTaskFinished)
    {
        if (RuntimeHelper.IsMSIX)
        {
            ApplicationData.Current.LocalSettings.Values[NotifyWhenTaskFinishedKey] = notifyWhenTaskFinished;
        }
        else
        {
            _notifyWhenTaskFinished = notifyWhenTaskFinished;
        }
    }


    public static TEnum GetEnum<TEnum>(string text) where TEnum : struct
    {
        if (!typeof(TEnum).GetTypeInfo().IsEnum)
        {
            throw new InvalidOperationException("Generic parameter 'TEnum' must be an enum.");
        }

        return (TEnum)Enum.Parse(typeof(TEnum), text);
    }


    public static T GetService<T>()
        where T : class
    {
        if ((Current as App)!.Host.Services.GetService(typeof(T)) is not T service)
        {
            throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
        }

        return service;
    }


    public static void SetCaptionButtonColor(Color color)
    {
        ResourceDictionary? resource = Current.Resources;
        resource["WindowCaptionForeground"] = color;
        MainWindow.AppWindow.TitleBar.ButtonForegroundColor = color;
    }


    public static void ApplySystemThemeToCaptionButtons()
    {
        if (MainWindow.Content is FrameworkElement root)
        {
            SetCaptionButtonColor(root.ActualTheme == ElementTheme.Dark ? Colors.White : Colors.Black);
        }
    }


    public static IProjectTask GetGlobalTask() => ((App)Current).GlobalTask;


    public static void BringToFront()
    {
        if (MainWindow.AppWindow is null)
        {
            return;
        }

        MainWindow.AppWindow.MoveInZOrderAtTop();
    }


    public static async Task ShowErrorMessage(Exception exception) =>
        await GetService<IMessageService>().ShowAsync(exception.Message, "错误");


    private static async void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        try
        {
            string logPath = Path.Combine(
                ApplicationData.Current.LocalFolder.Path,
                "CrashLogs.log");

            await File.AppendAllTextAsync(logPath,
                $"[{DateTime.Now}] Error: {e.Message}\r\n{e.Exception.StackTrace}\r\n");

            await GetService<IMessageService>().ShowAsync(
                string.Format(ResourceKeys.AppUnhandledExceptionMessage.GetLocalized(), logPath),
                ResourceKeys.AppErrorLabel.GetLocalized(),
                "打开目录",
                async void () =>
                {
                    try
                    {
                        await Launcher.LaunchFolderAsync(
                            await StorageFolder.GetFolderFromPathAsync(ApplicationData.Current.LocalFolder.Path));
                    }
                    catch (Exception exception)
                    {
                        await ShowErrorMessage(exception);
                    }
                });

            e.Handled = true;
        }
        catch (Exception exception)
        {
            await ShowErrorMessage(exception);
        }
    }

    protected async override void OnLaunched(LaunchActivatedEventArgs args)
    {
        try
        {
            base.OnLaunched(args);

            StartupWindow = new Window
            {
                SystemBackdrop = new MicaBackdrop(),
                Title = ResourceKeys.AppDisplayName.GetLocalized()
            };

            MainWindow.AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/WindowIcon.ico"));

            UiSettings.ColorValuesChanged += UISettings_ColorValuesChanged;
            WeakReferenceMessenger.Default.Register(GetService<TaskInformationViewModel>());
            GetService<IAppNotificationService>().Initialize();

            await PageInfoDataSource.Instance.InitPageInfoAsync();
            await GetService<IActivationService>().ActivateAsync(args);
        }
        catch (Exception e)
        {
            await ShowErrorMessage(e);
        }
    }


    private static string BuildPayload(string title, string detail, string actionContent, string actionArgument)
    {
        return string.Format(
            ResourceKeys.AppNotificationSamplePayload.GetLocalized(),
            AppContext.BaseDirectory,
            title,
            detail,
            actionContent,
            actionArgument);
    }


    public static void ShowNotification(string title, string message, Type targetType, string? detail)
    {
        GetService<IAppNotificationService>().Show(BuildPayload(title,
            message,
            "查看",
            $"target={targetType.FullName}{(!string.IsNullOrEmpty(detail) ? $";detail={detail}" : "")}"));
    }


    public static void ShowDirectoryNotification(string title, string message, string directory)
    {
        GetService<IAppNotificationService>().Show(
            BuildPayload(
                title,
                message,
                ResourceKeys.AppOpenFolderLabel.GetLocalized(),
                $"dir={directory}"));
    }


    public static void ShowErrorNotification(string message, Type targetType, string? detail = null) =>
        ShowNotification(ResourceKeys.AppErrorLabel.GetLocalized(), message, targetType, detail);
}